import { camelCase, upperFirst } from 'lodash-es'
import formatCode from '@/utils/formatCode'
const apiConfig = (apiJson) => {
  let tags = []
  let tagDesc = apiJson.tags || []

  if (apiConfig.tags) {
    tags = apiJson.tags || []
  } else {
    let p = apiJson.paths
    if (p) {
      let pathKeys = Object.keys(p)
      pathKeys.forEach((k) => {
        let pathItem = p[k]
        let itemKeys = Object.keys(pathItem)
        let method = itemKeys[0]
        let item = pathItem[method]

        let tag = item.tags ? item.tags[0] : null
        let t = tags.find(e => e.name === tag)
        if (!t && tag != null) {
          tags.push({
            name: tag,
            description: item.description,
          })
        } else {
          let description = tagDesc.find(e => e.name === tag)
          let tagItem = tags.find(e => e.name === tag)
          if (description) {
            tagItem.description = description.description
          }
        }
      })
    }
  }
  const paths = apiJson.paths || {}
  return {
    tags,
    paths,
    definitions: apiJson.definitions || {},
  }
}

const apiMapInit = (tags) => {
  const m = {}
  tags.map((tag) => {
    if (tag.name !== '') {
      m[tag.name] = []
    }
  })
  return m
}

const apiMap = (apiPrefix = '', paths, apiMap) => {
  const m = JSON.parse(JSON.stringify(apiMap))
  const dict = {
    新增: {
      prefix: 'add',
      label: '新增',
    },

    添加: {
      prefix: 'add',
      label: '添加',
    },

    修改: {
      prefix: 'update',
      label: '修改',
    },

    删除: {
      prefix: 'del',
      label: '删除',
    },

    详情: {
      prefix: 'detail',
      label: '详情',
    },
    列表: {
      prefix: 'list',
      label: '列表',
    },
    分页: {
      prefix: 'pagination',
      label: '分页',
    },
    记录: {
      prefix: 'record',
      label: '记录',
    },
    查询: {
      prefix: 'serach',
      label: '分页',
    },
    测试: {
      prefix: 'test',
      label: '测试',
    },
    get: {
      prefix: 'get',
      label: '新增',
    },
    post: {
      prefix: 'update',
      label: '修改',
    },
    put: {
      prefix: 'put',
      label: '修改',
    },
    delete: {
      prefix: 'del',
      label: '删除',
    },
  }
  // const dictKeys = Object.keys(dict);
  const keyword = [
    'export',
    'break',
    'else',
    'new',
    'var',
    'case',
    'finally',
    'return',
    'void',
    'catch',
    'for',
    'switch',
    'while',
    'continue',
    'function',
    'this',
    'with',
    'default',
    'if',
    'throw',
    'delete',
    'in',
    'try',
    'do',
    'instranceof',
    'typeof',
  ]
  // 分类所有api
  Object.keys(paths).forEach((path) => {
    const item = paths[path]
    let keyList = ['get', 'post', 'put', 'delete'].concat(Object.keys(item))
    let methodKey = keyList.filter((item, index, self) => {
      return self.indexOf(item) !== index
    })

    if (methodKey.length > 0) {
      const method = (methodKey[0] || '').toLocaleLowerCase()
      const tagName = item[method].tags[0] || '' // 所属文件夹
      const summary = item[method].summary ? `${item[method].summary}` : tagName // 接口简单描述
      const params = item[method].parameters || []
      const responses
        = item[method].responses
        && item[method].responses[200]
        && item[method].responses[200]['schema']
        && item[method].responses[200]['schema']['$ref']
          ? item[method].responses[200]['schema']['$ref']
          : {}
      let pathList = path.split('/')
      let lastPath
      if (!pathList[pathList.length - 1].includes('{')) {
        // 如果 path 不使用 路径参数, 使用路径最后的名字命名 api 函数名称
        lastPath = pathList[pathList.length - 1]
          ? pathList[pathList.length - 1].replace(new RegExp('-', 'g'), '')
          : ''
      } else {
        lastPath = pathList[pathList.length - 2]
          ? pathList[pathList.length - 2].replace(new RegExp('-', 'g'), '')
          : ''
      }
      // let key = dictKeys.find((key) => {
      //   let s = item[method].summary;
      //   if (s) {
      //     return s.includes(key);
      //   } else {
      //     return null;
      //   }
      // });
      let methodType = dict[method]
      let tagList = m[tagName]
      let apiName = lastPath.replace(lastPath[0], lastPath[0])
      tagList.push({
        responses,
        summary,
        path: apiPrefix + path,
        params,
        method,
        apiName: keyword.includes(apiName)
          ? methodType.prefix + lastPath.replace(lastPath[0], lastPath[0].toUpperCase())
          : lastPath.replace(lastPath[0], lastPath[0]),
      })
    }
  })
  return m
}

const apiDescriptions = (config) => {
  // 添加 参数 描述
  let paramsDoc = ''
  let entityName = ''
  config.params.forEach((p) => {
    let type
    if (p.type) {
      type = p.type
    } else {
      if (p.schema) {
        if (p.schema.originalRef) {
          type = 'object'
        } else if (p.schema.$ref) {
          type = 'object'
        } else {
          if (p.schema.type) {
            type = p.schema.type
          } else {
            type = ''
          }
        }
      } else {
        type = ''
      }
    }
    let pathList = config.path.split('/')
    // 取倒数第二个

    let structName = pathList.length >= 2 ? pathList[pathList.length - 2] : ''
    if (config.apiName === structName && pathList.length) {
      structName = pathList.length > 3 ? pathList[pathList.length - 3] : pathList[0]
    }
    entityName = structName

    // entityName = p.name
    paramsDoc
      += '\r\n'
      + `* @param { ${type} } ${p.name} - ${p.description} ( required: ${p.required || false} )`
  })

  return { desc: `\r* 【 ${config.summary} 】 ${paramsDoc}`, entityName }
}
const apiTmpStr = ({ requestName = 'request', config, definitions }) => {
  let definitionsArr = flatDefinitions(definitions)
  let paramsList = getParamsProperties(config, definitionsArr)

  if (paramsList && paramsList.length) {
    config.params = paramsList
  }
  // debugger
  let { params, dataConfig, pathParams } = apiParams(config)
  let { desc, entityName } = apiDescriptions(config)
  const requestConfig = {
    method: `${config.method}`,
    path: `${config.path || config.url}`,
  }

  let urlPath = getUrlPath(pathParams, requestConfig)
  let t = `
/**
  ${desc}
*/
  export const ${config.name || entityName} = (${getMethodsParams(requestConfig.method)}) => {
      return ${requestName}({
          url: ${urlPath},
          method: '${config.method}',
          ${getParamsOrDataStr(requestConfig.method, dataConfig)}
      })
  }

  `
  return t
}
const apiTemplate = ({ requestName = 'request', config, definitions }) => {
  // params in path, query, body

  let definitionsArr = flatDefinitions(definitions)

  let paramsList = getParamsProperties(config, definitionsArr)

  if (paramsList && paramsList.length) {
    config.params = paramsList
  }
  let { params, dataConfig, pathParams } = apiParams(config)
  let { desc, entityName } = apiDescriptions(config)

  const requestConfig = {
    method: `${config.method}`,
    path: `${config.path}`,
  }
  let urlPath = getUrlPath(pathParams, requestConfig)
  // let lengthFlag = params.split(',').length > 3 // 参数大于3个
  let t = `
/**
  ${desc}
*/
export const ${getEntityMethodName(
    requestConfig.method,
    config.apiName,
    entityName,
  )} = (${getMethodsParams(requestConfig.method)}) => {
    return ${requestName}({
        url: ${urlPath},
        method: '${requestConfig.method}',
        ${getParamsOrDataStr(requestConfig.method, dataConfig)}
      
    })
}
`
  return t
}
// mode route路径参数模式 query查询参数模式
function getUrlPath(pathParams, requestConfig, mode = 'route') {
  let urlPath = requestConfig.path
  if (pathParams?.length) {
    let replaceStr = getMethodsParams(requestConfig.method)

    if (mode === 'route' && replaceStr) {
      let urlStr = `\${${replaceStr}.`
      urlPath = urlPath.replace(/{/g, urlStr)
    }
  }

  const regex = /{+/ // 创建正则表达式，匹配一个或多个左花括号
  const hasPathParams = regex.test(urlPath) // 使用test方法测试匹配结果
  urlPath = hasPathParams ? `\`${urlPath}\`` : `'${urlPath}'`
  return urlPath
}

function getMethodsParams(method) {
  let str = 'data'
  if (method === 'get' || method === 'delete') {
    str = 'params'
  }
  return str
}

function getParamsOrDataStr(method, dataConfig) {
  let str = ''
  if (method === 'get' || method === 'delete') {
    str = 'params: params,'
    // str = dataConfig.params ? 'params: params,' : ''
  } else {
    str = 'data: data,'
    // str = dataConfig.data ? 'data: data,' : ''
    // str += dataConfig.params ? 'params: params' : ''
  }
  return str
}

const apiParams = (config) => {
  const { path, query, body } = apiConfigParams(config)
  let dataConfig = {}
  let params = ''
  if (path.length > 0) {
    params += path.join(', ')
  }

  if (query.length > 0) {
    let q = query.join(', ')
    dataConfig.params = `${q}`
    params += q
  }
  if (body.length > 0) {
    let d = body.join(', ')
    dataConfig.data = `${d}`
    params += d
  }

  return { params, dataConfig, pathParams: path }
}

const apiConfigParams = (config) => {
  let list = config.params
  let query = []
  let body = []
  let path = []

  list.forEach((p) => {
    let paramName = p.name
    let i = p.in
    if (i === 'path') {
      path.push(paramName)
    } else if (i === 'query') {
      query.push(paramName)
    } else if (i === 'body') {
      body.push(paramName)
    }
  })
  return { path, query, body }
}

const apiFile = (requestName, apiconfig, definitions) => {
  let apiList = []
  let keys = Object.keys(apiconfig)
  keys.forEach((k) => {
    let list = apiconfig[k]
    if (list) {
      apiList.push([
        [k],
        [...list.map(config => apiTemplate({ requestName, config, definitions }))],
      ])
    }
  })

  return apiList
}

const objectProperties = (definitions, label = false) => {
  let type = ['string', 'boolean', 'integer', 'number', 'object']
  let value = ['\'\'', false, null, null, '{}']
  let keys = Object.keys(definitions)
  let data = ''
  let dataLabel = ''
  keys.forEach((k) => {
    let item = definitions[k]
    let oKey = k
    // 判断 id 只获取实体类
    if (item.properties && Object.prototype.hasOwnProperty.call(item.properties, 'id')) {
      if (item.type === 'object') {
        let properties = item.properties
        let propKeys = Object.keys(properties)
        let o = ''
        let m = ''
        propKeys.forEach((pkey) => {
          let pItem = properties[pkey]
          let index = type.findIndex(e => e === pItem.type)
          if (index !== -1) {
            o += `'${pkey}': ${value[index]}, // ${pItem.description}\r\n`
          } else {
            o += `'${pkey}': null,\r\n`
          }
          m += `'${pkey}': { prop: '${pkey}', label: '${pItem.description}' }, // ${pItem.description}\r\n`
        })
        let obj = `{${o}}`
        data += `'${oKey.slice(0).toLocaleLowerCase() + oKey.slice(1)}': ${obj},
                `
        if (label === true) {
          let objLabel = `{${m}}`
          dataLabel += `'${oKey.slice(0).toLocaleLowerCase() + oKey.slice(1)}': ${objLabel},
                  `
        }
      }
    }
  })
  let item = {
    modelData: `{${data}}`,
  }
  if (label === true) {
    item.dataLabel = `{${dataLabel}}`
  }
  return item
}
const getSwaggerTree = (apiJson, apiPrefix = '') => {
  const { tags, paths, definitions } = apiConfig(apiJson)
  let m = apiMapInit(tags)
  let data = apiMap(apiPrefix, paths, m)
  return { data, definitions }
}

const changSwaggerData = async ({
  requestName = 'request',
  apiPath,
  json = null,
  apiPrefix = '',
  importPath,
  modelLabel = false,
  showModelPath = false,
}, modelList = []) => {
  let apiJson = {}
  if (json) {
    apiJson = json
  } else {
    apiJson = await fetch(apiPath).then(res => res.json())
  }

  let { data, definitions } = getSwaggerTree(apiJson, apiPrefix)

  let list = apiFile(requestName, data, definitions)

  let { modelData, dataLabel } = objectProperties(definitions, modelLabel)

  try {
    let option = {
      indent_size: 4,
      space_in_empty_paren: true,
    }
    let allData = {
      api: {},
    }

    list.forEach((file) => {
      let fileName = file[0]
      let requestPath = importPath ? `${importPath}\r\n\r\n` : ''
      let itemConfig = modelList.find(x => `${x.tableCnName}` === `${fileName}`)

      let exportFunStr = itemConfig?.apiList?.map(x => x.name).join(',')
      let tableName = itemConfig?.tableName || (fileName || 'default')
      let tips = showModelPath
        ? `
/**
  导出${tableName}接口 \n
  import  { ${exportFunStr} } from "@/api/${tableName}/index" \r\n
*/
      \n
        `
        : ''
      let fileContent = requestPath + tips + file[1].join('')

      allData.api[fileName] = fileContent
    })
    // const formatModelData = beautify(modelData, option);

    allData.model = modelData
    if (modelLabel === true) {
      // const formatModelLabelData = beautify(dataLabel, option);
      const formatModelLabelData = dataLabel
      allData.modelLabel = formatModelLabelData
    }
    return allData
  } catch (err) {
    console.error(err)
  }
}
const getEntityMethodName = (method, apiName, entityName = '') => {
  if (!apiName) {
    method = 'query'
    apiName = entityName || `${method}Entity`
  }
  // postAdd
  let structName
    = apiName.length > 4 ? '' : entityName && entityName !== apiName ? upperFirst(entityName) : ''
  // if (!structName) {
  //   structName = entityName
  // }

  // 实体和method组成函数名称
  return camelCase(method + structName + upperFirst(apiName))
}

// 转换definitions为数组
const flatDefinitions = (definitions) => {
  let arr = []
  for (let i in definitions) {
    let obj = definitions[i]
    arr.push(obj)
  }
  return arr
}
// 获取参数body的对应实体属性
const getParamsProperties = (x, definitions) => {
  let paramsList = []
  if (x?.params && x?.params?.length) {
    let schemaList = x.params.filter(x => x.schema)

    if (schemaList?.length) {
      schemaList.forEach((item) => {
        if (item?.schema?.$ref) {
          let schemaInfo = item.schema.$ref.replace('#/definitions/', '')
          let info = definitions.find(x => x.title == schemaInfo)
          if (info && info.properties) {
            paramsList = changeProperties(info.properties, item.in)
          }
        }
      })
    }
  }
  return paramsList
}

const changeProperties = (properties, inType) => {
  let arr = []
  for (let i in properties) {
    let obj = properties[i]
    arr.push({
      name: i,
      type: obj.type,
      description: obj.description,
      required: obj.required || !obj.allowEmptyValue,
      in: inType || 'body',
    })
  }
  return arr
}
// 处理出参
const getSwaggerResponse = (x, definitions) => {
  let responseList = []
  if (x && typeof x == 'string') {
    let item = x.replace('#/definitions/', '')
    let info = definitions.find(x => x.title == item)
    if (info?.properties?.data) {
      if (info?.properties?.data?.$ref) {
        let dataInfo = info.properties.data.$ref.replace('#/definitions/', '')
        let dataInfoObj = definitions.find(x => x.title == dataInfo)

        if (dataInfoObj?.properties) {
          if (
            !dataInfoObj?.properties?.records
            || !dataInfoObj?.properties.records?.type
            || !dataInfoObj?.properties?.records?.items
          ) {
            let list = changeProperties(dataInfoObj.properties, 'response')

            list && responseList.push(list)
          } else if (dataInfoObj?.properties?.records?.type == 'array') {
            let items = dataInfoObj.properties.records.items
            let records = []

            if (items?.$ref) {
              let itemsInfo = items.$ref.replace('#/definitions/', '')
              let itemsInfoObj = definitions.find(x => x.title == itemsInfo)

              if (itemsInfoObj?.properties) {
                records = changeProperties(itemsInfoObj.properties, 'response')
              }
              dataInfoObj.properties.records = records
            }

            for (let i in dataInfoObj.properties) {
              responseList.push({ [i]: dataInfoObj.properties[i] })
            }
          }
        }
      } else {
        let dataInfoObj = info.properties.data
        if (!dataInfoObj) {
          return []
        }
        if (dataInfoObj?.items?.$ref) {
          let itemsRef = dataInfoObj.items.$ref.replace('#/definitions/', '')
          let itemsRefObj = definitions.find(x => x.title == itemsRef)

          if (itemsRefObj?.properties) {
            responseList.push(changeProperties(itemsRefObj.properties, 'response'))
          }
        } else {
          for (let i in dataInfoObj) {
            responseList.push({ [i]: dataInfoObj[i] })
          }
        }
      }
    }
  }
  return responseList
}

export {
  changSwaggerData,
  getSwaggerTree,
  getEntityMethodName,
  apiParams,
  changeProperties,
  flatDefinitions,
  getParamsProperties,
  getSwaggerResponse,
  apiTemplate,
  apiTmpStr,
  getMethodsParams,
}
